package com.crane.oss.common.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crane.common.core.exception.AppException;
import com.crane.common.core.exception.BasicErrorCode;
import com.crane.common.core.exception.BizException;
import com.crane.common.core.utils.CollectionUtil;
import com.crane.common.core.utils.StringUtil;
import com.crane.common.framework.utils.BeanUtil;
import com.crane.common.framework.utils.DateUtil;
import com.crane.oss.common.bean.request.AddDynamicCodeRequest;
import com.crane.oss.common.bean.request.QueryDynamicCodeRequest;
import com.crane.oss.common.dao.DynamicCodeDao;
import com.crane.oss.common.model.District;
import com.crane.oss.common.model.DynamicCode;
import com.crane.oss.properties.SmsConfigProperties;
import com.crane.oss.common.service.DynamicCodeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

@SuppressWarnings("deprecation")
@Service("dynamicCodeService")
public class DynamicCodeServiceImpl implements DynamicCodeService {

    protected static final String DICT_TEMPLATE_VERIFY_CODE = "sms_template_verify_code";
    protected static final String DICT_VERIFY_CODE_LIMIT = "sms_verify_code_limit";
    protected static final int DEFAULT_VERIFY_CODE_LIMIT = 10;
    private static final Logger logger = LoggerFactory.getLogger(DynamicCodeServiceImpl.class);

    @Resource
    protected DynamicCodeDao dynamicCodeDao;
    @Resource
    protected SmsConfigProperties dbkjConfigProperties;

	@Override
    public void validateCode(QueryDynamicCodeRequest request) {
        logger.info("DynamicCodeServiceImpl validateCode request={}", JSON.toJSON(request));

        // 短信打开状态为false, 不验证
        if (!dbkjConfigProperties.isSmsOpenFlag()) {
            logger.info("DynamicCodeServiceImpl validateCode mock response=true");
            return;
        }

        Date now = new Date();
        IPage<DynamicCode> page = dynamicCodeDao.selectPage(new Page<>(0, 1),new LambdaQueryWrapper<DynamicCode>().eq(ObjectUtils.isNotNull(request.getUserId()),DynamicCode::getUserId,request.getUserId())
        .eq(DynamicCode ::getBizType,request.getBizType())
        .eq(DynamicCode ::getRefNo,request.getRefNo())
        .orderByDesc(DynamicCode ::getId));;
        if (page.getRecords().isEmpty()) {
            throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "短信验证码");
        }
        DynamicCode dbCode = page.getRecords().get(0);
        Integer failCount = dbCode.getFailCount();
        if (dbCode.getExpiredTime().before(now)) {
            throw new BizException("验证码过期");
        }
        // 并发超出影响不大
        if (dbCode.getFailLimit().compareTo(failCount) <= 0) {
            throw new BizException("失败次数过多");
        }
        if (!dbCode.getDynaCode().equals(request.getDynaCode())) {
        	dynamicCodeDao.updateFailCount(dbCode.getId());
            throw new BizException("验证码错误");
        }
        DynamicCode dynamiccode = new DynamicCode();
        dynamiccode.setId(dbCode.getId());
        dynamiccode.setExpiredTime(now);
        dynamiccode.setUpdateTime(now);
        int rs = dynamicCodeDao.updateById(dynamiccode);
        logger.info("DynamicCodeServiceImpl validateCode response={}", rs);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String getCode(AddDynamicCodeRequest addRequest, String signature) {
        logger.info("DynamicCodeServiceImpl getCode request={}", JSON.toJSON(addRequest));

        // 短信打开状态为false, 不发送验证码
        if (!dbkjConfigProperties.isSmsOpenFlag()) {
            logger.info("DynamicCodeServiceImpl getCode mock response=999999");
            return "999999";
        }
        String dynaCode = String.format("%04d", new Random().nextInt(9999));
        ;
        Date now = new Date();
        Map<String, Object> map = new HashMap<>();
        map.put("refNo", addRequest.getPhone());
		map.put("beginTime", DateUtil.getStartDateStr(new Date()));
		map.put("endTime", DateUtil.getEndDateStr(new Date()));

        //同一手机号单日短信次数限制，默认10次
        String verifyCodeLimit = "10";
        int totalLimit = DEFAULT_VERIFY_CODE_LIMIT;
        if (StringUtil.isNotEmpty(verifyCodeLimit)) {
            totalLimit = Integer.valueOf(verifyCodeLimit);
        }

        int totalCnt = dynamicCodeDao.selectCount(new LambdaQueryWrapper<>());
        if (totalCnt >= totalLimit) {
            logger.error("DynamicCodeServiceImpl Send VerifyCode to Max Limit[totalLimit={}, totalCnt={}].", totalLimit, totalCnt);
            throw new BizException("超过最大限制");
        }
        if (null != addRequest.getUserId()) {
            map.put("userId", addRequest.getUserId());
        }
        map.put("bizType", addRequest.getBizType());
        map.put("offset", 0);
        map.put("limit", 1);

        List<DynamicCode> list = dynamicCodeDao.selectList(Wrappers.lambdaQuery(DynamicCode.class)
                .eq(DynamicCode::getBizType,addRequest.getBizType())
        .eq(ObjectUtils.isNotNull(addRequest.getUserId()),DynamicCode::getUserId,addRequest.getUserId()));
        DynamicCode dbCode = null;
        if (CollectionUtil.isEmpty(list)) {
            dbCode = bulideDynamicCode(addRequest);
            dbCode.setDynaCode(dynaCode);
            dynamicCodeDao.insert(dbCode);
        } else {
            dbCode = list.get(0);
        }

        if (dbCode.getExpiredTime().before(now)) {
            DynamicCode dynamiccode = bulideDynamicCode(addRequest);
            dynamiccode.setDynaCode(dynaCode);
            dynamicCodeDao.insert(dynamiccode);
        } else {
            DynamicCode dynamiccode = new DynamicCode();
            dynamiccode.setId(dbCode.getId());
            dynamiccode.setExpiredTime(new Date(now.getTime() + 5 * 60 * 1000));
            dynamiccode.setUpdateTime(now);
            dynamicCodeDao.updateById(dynamiccode);
            dynaCode = dbCode.getDynaCode();
        }
        logger.info("DynamicCodeServiceImpl getCode response={}", dynaCode);
        return dynaCode;
    }

    private DynamicCode bulideDynamicCode(AddDynamicCodeRequest addRequest) {
        Date now = new Date();
        DynamicCode dynamiccode = new DynamicCode();
        BeanUtil.copyProperties(addRequest, dynamiccode);
        if (null == addRequest.getExpiredTime()) {
            dynamiccode.setExpiredTime(new Date(now.getTime() + 5 * 60 * 1000));
        }
        if (null == addRequest.getFailLimit()) {
            dynamiccode.setFailLimit(5);
        }
        dynamiccode.setRefNo(addRequest.getPhone());
        dynamiccode.setCreateTime(now);
        dynamiccode.setFailCount(0);
        return dynamiccode;
    }
}
